package com.duguo.dynamicmvc.factory.processor.model;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtPrimitiveType;
import javassist.bytecode.annotation.AnnotationImpl;

import com.duguo.dynamicmvc.factory.ControllerFactory;
import com.duguo.dynamicmvc.factory.utils.javassist.FieldUtils;
import com.duguo.dynamicmvc.factory.utils.javassist.MethodUtils;
import com.duguo.dynamicmvc.model.validator.exception.ValidationException;

public class ModelValidationCreatorImpl implements ModelValidationCreator{
	
	
	public boolean hasValidationException(CtMethod ctMethod)throws Exception {
		for(CtClass exception:ctMethod.getExceptionTypes()){
			if(exception==ClassPool.getDefault().get(ValidationException.class.getName())){
				return true;
			}
		}
		return false;
	}
	
	public boolean hasValidationException(CtClass modelImplCtClass)throws Exception {
		CtMethod bindCtMethod=MethodUtils.findMethod(modelImplCtClass, "bind");
		for(CtClass exception:bindCtMethod.getExceptionTypes()){
			if(exception==ClassPool.getDefault().get(ValidationException.class.getName())){
				return true;
			}
		}
		return false;
	}

	public void addValidationExceptionStart(StringBuffer methodBody, StringBuffer validationException, boolean hasException) {
		if(hasException){
			methodBody.append("try{");
			validationException.append("true");
		}
	}


	public void addValidationExceptionEnd(String fieldName,StringBuffer methodBody, boolean hasException) {
		if(hasException){
			methodBody.append("}catch("+ValidationException.class.getName()+" ex){");
			initExceptionCode(methodBody);
			methodBody.append("validationException.addResult(\""+fieldName+"\",ex);}");
		}
	}


	public void addNumberFomratValidator(ControllerFactory controllerFactory,String fieldName, StringBuffer methodBody, boolean isNumber, CtPrimitiveType fieldPrimitiveType,CtClass modelImplCtClass,Map<String, CtClass> usedParams, CtMethod controllerMethod,Map<String,Object> staticObjects)  throws Exception{
		if(isNumber){
			Map<String,Object> setters=new HashMap<String,Object>();
			setters.put("numberType", fieldPrimitiveType.getSimpleName());			
			methodBody.append("}catch("+NumberFormatException.class.getName()+" ex){");
			initExceptionCode(methodBody);
			String validatorName=fieldPrimitiveType.getSimpleName();
			addValidator(controllerFactory,validatorName,setters,fieldName,"tempStringValue",modelImplCtClass, usedParams, methodBody, controllerMethod,staticObjects);
			methodBody.append("}");
		}
	}


	public void addNoneRequiredValidators(ControllerFactory controllerFactory,String fieldName,CtClass modelImplCtClass, Map<String, CtClass> usedParams, StringBuffer methodBody, CtMethod controllerMethod,Map<String,Object> staticObjects, List validators, ModelAnnotationReader modelAnnotationReader) throws Exception {
		List<Object> noneRequiredValidators=modelAnnotationReader.getNoneRequiredValidators(validators);
      	if(noneRequiredValidators.size()>0){
      		for(Object obj:noneRequiredValidators){
      			addValidator(controllerFactory,(AnnotationImpl)obj,fieldName,"this."+fieldName,modelImplCtClass, usedParams, methodBody, controllerMethod,staticObjects, modelAnnotationReader);
      		}
      	}
	}



	public void addThrowValidationException(StringBuffer validationException, StringBuffer methodBody) {
		if(validationException.length()>0){
			methodBody.insert(0, ValidationException.class.getName()+" validationException=null;");
			methodBody.append("if(validationException!=null) throw validationException;");
		}
	}


	public void addRequiredValidator(ControllerFactory controllerFactory,String fieldName,CtClass modelImplCtClass, Map<String, CtClass> usedParams, StringBuffer methodBody, CtMethod controllerMethod,Map<String,Object> staticObjects, List validators, ModelAnnotationReader modelAnnotationReader) throws Exception {
		AnnotationImpl annotationImpl=modelAnnotationReader.getRequiredValidator(validators);
		if(annotationImpl!=null){
			methodBody.append("else{");
			addValidator(controllerFactory,annotationImpl,fieldName,"tempStringValue",modelImplCtClass, usedParams, methodBody, controllerMethod,staticObjects, modelAnnotationReader);
			methodBody.append("}");
		}
	}

	void addValidator(ControllerFactory controllerFactory,AnnotationImpl annotationImpl,String fieldName,String valueStr,CtClass modelImplCtClass, Map<String, CtClass> usedParams, StringBuffer methodBody, CtMethod controllerMethod,Map<String,Object> staticObjects , ModelAnnotationReader modelAnnotationReader)throws Exception  {
		String validatorName=modelAnnotationReader.getValidatorName(annotationImpl);
		addValidator(controllerFactory,validatorName,null,fieldName,valueStr,modelImplCtClass, usedParams, methodBody, controllerMethod,staticObjects);
	}
	
	void addValidator(ControllerFactory controllerFactory,String validatorName,Map<String,Object> setters,String fieldName,String valueStr,CtClass modelImplCtClass, Map<String, CtClass> usedParams, StringBuffer methodBody, CtMethod controllerMethod,Map<String,Object> staticObjects )throws Exception  {
		Object validator=getParameterValidator(controllerFactory,validatorName);
		if(validator==null){
			throw new RuntimeException("Cannot find validator "+validatorName);
		}
		validatorName=validatorName+"Validator";
		CtClass validatorCtClass=ClassPool.getDefault().get(validator.getClass().getName());
		if(!staticObjects.containsKey(validatorName)){
			FieldUtils.setupStaticField(modelImplCtClass, validatorCtClass, validatorName,validator);
			staticObjects.put(validatorName, validator);
		}
		methodBody.append(validatorName+".validate(");
		methodBody.append(valueStr);
		methodBody.append(");");
	}


	void initExceptionCode(StringBuffer methodBody) {
		methodBody.append("if(validationException==null) validationException=new "+ValidationException.class.getName()+"();");
	}
	
	



	private Object getParameterValidator(ControllerFactory controllerFactory, String validatorName) throws Exception {
		if(controllerFactory.getWorkingModel().getParameterValidators()!=null){
			if(controllerFactory.getWorkingModel().getParameterValidators().containsKey(validatorName))
				return controllerFactory.getWorkingModel().getParameterValidators().get(validatorName);
		}
		if(controllerFactory.getWorkingModel().isRootController()){
			if(controllerFactory.getControllerBuilder().getGloableParameterValidators()!=null){
				if(controllerFactory.getControllerBuilder().getGloableParameterValidators().containsKey(validatorName))
					return controllerFactory.getControllerBuilder().getGloableParameterValidators().get(validatorName);
			}
		}else{
			return getParameterValidator(controllerFactory.getWorkingModel().getParentWorkModel().getControllerFactory(), validatorName);
		}
		return null;
	}


}
